/* Sentinel - IRC Statistical and Operator Services
** split-util.c - Split service's utility functions
**
** Copyright W. Campbell and others.  See README for more details
** Some code Copyright: Jonathan George, Kai Seidler, ircd-hybrid Team,
**                      IRCnet IRCD developers.
**
** $Id: split-util.c 7 2010-02-01 19:17:42Z dubkat $
*/

#include "config.h"
#include "stats.h"
#include "struct.h"
#include "proto.h"
#include "slog.h"

#ifdef SPLITSERV

#define DASH_TERM  0
#define DASH_BLANK 1
#define DASH_BAR   2
#define DASH_MID   3
#define DASH_FINAL 4

static char* dash(int *);
static char* hdash(int *);
static void dump_text(FILE*, struct SpServer*, int, int *);

/* This typedef is a hack around an astyle bug */
typedef struct mapreturn mapret;

void dump_map()
{
  FILE *fp = fopen(sp.text_name, "w");
  dump_text(fp, map_head, 0, NULL);
  fclose(fp);
}

static void dump_text(FILE *fp, struct SpServer *s, int level, int *values)
{
  /* For documentation look in PrintMap() */
  char *d;
  dlink_node *dl;
  int count = 0;
  struct SpServer *stmp;

  if (s)
  {
    if (level == 0)
    {
      values = calloc(MAXDEPTH + 1, sizeof(int));
      values[0] = DASH_TERM;
    }
    d = dash(values);
    fprintf(fp, "%s %s\n", d, s->name);
    DLINK_FOREACH(dl, s->leaflist.head)
    {
      stmp = (struct SpServer *) dl->data;
      if ((level > 0) && (values[level - 1] == DASH_MID))
      {
        values[level - 1] = DASH_BAR;
      }
      else if ((level > 0) && (values[level - 1] == DASH_FINAL))
      {
        values[level - 1] = DASH_BLANK;
      }
      else if ((level > 0) && (values[level - 1] == DASH_TERM))
      {
        values[level - 1] = DASH_BLANK;
      }
      count++;
      if (count == s->numleaf)
      {
        values[level] = DASH_FINAL;
      }
      else
      {
        values[level] = DASH_MID;
      }
      values[level + 1] = DASH_TERM;

      dump_text(fp, (struct SpServer *) dl->data, level + 1,
                values);
    }
    if (level == 0)
    {
      free(values);
    }
  }
}

void html_map(FILE *fp, struct SpServer *s, int level, int *values)
{
  /* For documentation look in PrintMap() */
  char *d;
  dlink_node *dl;
  int count = 0;
  struct SpServer *stmp;

  if (s)
  {
    if (level == 0)
    {
      values = calloc(MAXDEPTH + 1, sizeof(int));
      values[0] = DASH_TERM;
    }
    d = hdash(values);
    fprintf(fp, "%s <A HREF=\"#%s\">%s</A><BR>\n", d, s->name,
            s->name);
    DLINK_FOREACH(dl, s->leaflist.head)
    {
      stmp = (struct SpServer *) dl->data;
      if ((level > 0) && (values[level - 1] == DASH_MID))
      {
        values[level - 1] = DASH_BAR;
      }
      else if ((level > 0) && (values[level - 1] == DASH_FINAL))
      {
        values[level - 1] = DASH_BLANK;
      }
      else if ((level > 0) && (values[level - 1] == DASH_TERM))
      {
        values[level - 1] = DASH_BLANK;
      }
      count++;
      if (count == s->numleaf)
      {
        values[level] = DASH_FINAL;
      }
      else
      {
        values[level] = DASH_MID;
      }
      values[level + 1] = DASH_TERM;

      html_map(fp, (struct SpServer *) dl->data, level + 1,
               values);
    }
    if (level == 0)
    {
      free(values);
    }
  }
}

static char* dash(int* values)
{
  int i = 0;
  static char s[MAXDEPTH * 4];

  /* Terminate it */
  s[0] = '\0';

  if ((values == NULL) || (values[0] == DASH_TERM))
  {
    return s;
  }

  /* KLUDGE...add a space so the map is indented correctly */
  s[0] = ' ';
  s[1] = '\0';

  while (values[i] != DASH_TERM)
  {
    switch (values[i])
    {
      case DASH_BLANK:
        strlcat(s, "   ", MAXDEPTH * 4);
        break;
      case DASH_BAR:
        strlcat(s, "|  ", MAXDEPTH * 4);
        break;
      case DASH_MID:
        strlcat(s, "|-", MAXDEPTH * 4);
        break;
      case DASH_FINAL:
        strlcat(s, "`-", MAXDEPTH * 4);
        break;
      default:
        break;
    }
    i++;
  }
  return s;
}

static char* hdash(int* values)
{
  int i = 0;
  static char s[MAXDEPTH * 10];

  /* Terminate it */
  s[0] = '\0';

  if ((values == NULL) || (values[0] == DASH_TERM))
  {
    return s;
  }

  /* KLUDGE...add a space so the map is indented correctly */
  strlcat(s, "&nbsp;", MAXDEPTH * 10);

  while (values[i] != DASH_TERM)
  {
    switch (values[i])
    {
      case DASH_BLANK:
        strlcat(s, "&nbsp;&nbsp;&nbsp;", MAXDEPTH * 10);
        break;
      case DASH_BAR:
        strlcat(s, "|&nbsp;&nbsp;", MAXDEPTH * 10);
        break;
      case DASH_MID:
        strlcat(s, "|-", MAXDEPTH * 10);
        break;
      case DASH_FINAL:
        strlcat(s, "`-", MAXDEPTH * 10);
        break;
      default:
        break;
    }
    i++;
  }
  return s;
}

mapret PrintMap(char* target, struct SpServer* s, int level,
                int *values)
{
  char* d;
  dlink_node *dl;
  int maxdepth = level;
  int totalservers = 0;
  struct mapreturn retval;
  int count = 0;
  struct SpServer *stmp;

  if (s)
  {
    if (level == 0)
    {
      /* This is the initial call, values was passed in as
      ** NULL and as such has not been created.
      */
      values = calloc(MAXDEPTH + 1, sizeof(int));
      /* level 0 is always myself, and there is only
      ** ever one item at level 0
      */
      values[0] = DASH_TERM;
    }
    d = dash(values);
    notice(target, SplitServ.nick, "(%d) %s %s", level, d,
           s->name);
    totalservers++;
    select_delay();
    DLINK_FOREACH(dl, s->leaflist.head)
    {
      stmp = (struct SpServer *) dl->data;
      /* We check for what was set for this value,
      ** and reset it to one of the "non-terminal" items */
      if ((level > 0) && (values[level - 1] == DASH_MID))
      {
        values[level - 1] = DASH_BAR;
      }
      else if ((level > 0) && (values[level - 1] == DASH_FINAL))
      {
        values[level - 1] = DASH_BLANK;
      }
      else if ((level > 0) && (values[level - 1] == DASH_TERM))
      {
        values[level - 1] = DASH_BLANK;
      }
      /* so that it's 1 the first time through the loop */
      count++;
      /* Now we need to adjust the values array for each
      ** leaf
      */
      if (count == s->numleaf)
      {
        values[level] = DASH_FINAL;
      }
      else
      {
        values[level] = DASH_MID;
      }
      values[level + 1] = DASH_TERM;

      retval = PrintMap(target, stmp, level + 1, values);
      if (retval.depth > maxdepth)
        maxdepth = retval.depth;
      totalservers += retval.count;
    }
    if (level == 0)
    {
      /* I've printed the map and I'm at the head of
      ** the recursive descent.  If I'm in here, we're
      ** ready to print the summary line.
      */
      notice(target, SplitServ.nick,
             "%d Total servers, max depth %d",
             totalservers, maxdepth);

      free(values);
    }
  }

  retval.count = totalservers;
  retval.depth = maxdepth;
  return retval;
}

void PrintSplit(char* target)
{
  int count = 0;
  char * td;
  dlink_node *dl;
  struct SpServer *spl;

  /* Everything in the Split List will have been created with
     NewSplit, so it will have a valid time stamp */

  DLINK_FOREACH(dl, split_table.head)
  {
    spl = (struct SpServer *) dl->data;
    td = timediff(CurrentTime - spl->ts);
    count++;
    notice(target, SplitServ.nick, "SPLIT SERVER: %s for %s",
           spl->name, td);
    select_delay();
  }
  if (count)
  {
    notice(target, SplitServ.nick, "%d TOTAL SPLITS", count);
  }
  else
  {
    notice(target, SplitServ.nick, "NO SPLITS");
  }
}

void PrintDump(char * target)
{
  char leaves[BUFSIZE] = "";
  dlink_node *dl;
  struct SpServer *s;
  dlink_node *dupl;
  struct SpServer *sup;

  DLINK_FOREACH(dl, server_table.head)
  {
    s = (struct SpServer *) dl->data;
    leaves[0] = '\0';
    if (s != NULL)
    {
      DLINK_FOREACH(dupl, s->leaflist.head)
      {
        sup = (struct SpServer *) dupl->data;
        strlcat(leaves, " ", BUFSIZE);
        strlcat(leaves, sup->name, BUFSIZE);
      }
      notice(target, SplitServ.nick, "Server: %s Leaves:%s",
             s->name,
             (!strcmp(leaves, "")) ? " None" : leaves);
      select_delay();
    }
  }
}

void expire_splits(void)
{
  struct SpServer * s = NULL;
  dlink_node *dl;
  dlink_node *tdl;

  DLINK_FOREACH_SAFE(dl, tdl, split_table.head)
  {
    s = (struct SpServer *) dl->data;
    if ((CurrentTime - s->ts) > sp.expire_time)
    {
      dlink_delete(dl, &split_table);
      if (IsSPReport())
        notice(sp.join_chan, SplitServ.nick,
               "SPLIT EXPIRED AFTER %s: %s",
               timediff(CurrentTime - s->ts), s->name);
      slog(SPLIT_LOG, L_NOTICE, "Split %s expired after %s",
           s->name, timediff(CurrentTime - s->ts));
      free(s);
      dlink_free(dl);
      /* Can't use freed memory...expecially on Linux...
      ** one thing they did do right...
      */
    }
  }
}

#endif
